package main

import (
	"bytes"
	"context"
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"gitee.com/zhangxinzx/im-related/goim"
)

const (
	GOIM_ADDR    = "localhost:3111"
	GOIM_NETWORK = "tcp"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go fakeGoimServer(ctx, GOIM_NETWORK, GOIM_ADDR)

	time.Sleep(500 * time.Millisecond) // wait fake server to be ready
	conn, err := net.Dial(GOIM_NETWORK, GOIM_ADDR)
	if err != nil {
		fmt.Println("goim: client dial failed with ", err)
		return
	}
	defer conn.Close()

	encoder := goim.NewGoimEncoder(conn)
	req := &goim.Proto{
		Ver:  1,
		Op:   goim.OpSendMsg,
		Seq:  1,
		Body: []byte("Hello"),
	}
	err = encoder.Write(req)
	if err != nil {
		fmt.Println("goim: create request to server failed with ", err)
		return
	}

	decoder := goim.NewGoimDecoder(conn)
	resp, err := decoder.Read()
	if err != nil {
		fmt.Println("goim: read response from server failed with ", err)
		return
	}
	fmt.Println("goim: client recieved message version ", resp.Ver, " Op: [", resp.Op, "] seq: ", resp.Seq, " body: ", string(resp.Body))
	cancel()
	time.Sleep(time.Second)
}

func fakeGoimServer(ctx context.Context, network, addr string) {

	listener, err := net.Listen(network, addr)
	if err != nil {
		panic(err)
	}
	exitChan := make(chan bool, 1)
	handleExit := func() {
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
		for {
			select {
			case s := <-sigChan:
				fmt.Println("goim: server recived signal ", s, " exiting...")
				listener.Close()
				exitChan <- true
				return
			case <-ctx.Done():
				fmt.Println("goim: server ctx done with [", ctx.Err(), "] exiting...")
				listener.Close()
				exitChan <- true
				return
			}
		}
	}
	go handleExit()

	for {
		conn, err := listener.Accept()
		if err != nil {
			select {
			case <-exitChan:
				fmt.Println("goim: server listener exited.")
				return
			default:
			}
			continue
		}

		go handleClientConn(conn)
	}

}

func handleClientConn(conn net.Conn) {
	fmt.Println("goim: server recieved message from [ ", conn.RemoteAddr().String(), " ]")
	defer conn.Close()

	decoder := goim.NewGoimDecoder(conn)
	req, err := decoder.Read()
	if err != nil {
		fmt.Println("goim: server decoder failed with ", err)
		return
	}
	fmt.Println("goim: server recieved message version ", req.Ver, " Op: [", req.Op, "] seq: ", req.Seq, " body: ", string(req.Body))

	encoder := goim.NewGoimEncoder(conn)
	btsBuff := bytes.NewBuffer(nil)
	btsBuff.Write(req.Body)
	btsBuff.Write([]byte(" recieved"))

	respProto := &goim.Proto{
		Ver: 1,
		Op:  goim.OpSendMsgReply,
	}
	respProto.Body = btsBuff.Bytes()
	respProto.Seq = req.Seq
	err = encoder.Write(respProto)
	if err != nil {
		fmt.Println("goim: server encoder failed with ", err)
		return
	}
}
